<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> 
    <title> - 天地维杰网</title>
    <meta name="keywords" content="系统架构,shutdown,不与天斗,Domino,博客,程序员,架构师,笔记,技术,分享,java,Redis">
    
    <meta property="og:title" content="">
    <meta property="og:site_name" content="天地维杰网">
    <meta property="og:image" content="/img/author.jpg"> 
    <meta name="title" content=" - 天地维杰网" />
    <meta name="description" content="天地维杰网 | 博客 | 软件 | 架构 | Java "> 
    <link rel="shortcut icon" href="http://www.shutdown.cn/img/favicon.ico" />
    <link rel="apple-touch-icon" href="http://www.shutdown.cn/img/apple-touch-icon.png" />
    <link rel="apple-touch-icon-precomposed" href="http://www.shutdown.cn/img/apple-touch-icon.png" />
    <link href="http://www.shutdown.cn/js/vendor/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/js/vendor/fancybox/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/css/main.css" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/css/syntax.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" id="hexo.configuration">
  var NexT = window.NexT || {};
  var CONFIG = {
    scheme: 'Pisces',
    sidebar: {"position":"left","display":"post"},
     fancybox: true, 
    motion: true
  };
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-7826003325059020" crossorigin="anonymous"></script>
</head>
<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
<div class="container one-collumn sidebar-position-left page-home  ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"> <div class="site-meta  custom-logo ">

  <div class="custom-logo-site-title">
    <a href="http://www.shutdown.cn"  class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <span class="site-title">天地维杰网</span>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>
  <p class="site-subtitle">人如秋鸿来有信，事若春梦了无痕</p>
</div>

<div class="site-nav-toggle">
  <button>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
  </button>
</div>

<nav class="site-nav">
    <ul id="menu" class="menu">
      
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />首页
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/redis/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-battery-full"></i> <br />Redis
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/java/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-coffee"></i> <br />java
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/linux/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-linux"></i> <br />linux
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/daily/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-bug"></i> <br />日常问题
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/spring/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-child"></i> <br />Spring和Springboot
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/mac/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-fire"></i> <br />Mac相关
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/middleware/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-gavel"></i> <br />中间件
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/jiagou/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-rocket"></i> <br />架构
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/python/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-ship"></i> <br />python
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/front/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-bolt"></i> <br />前端
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/jvm/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-balance-scale"></i> <br />jvm
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/c/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-battery-empty"></i> <br />c语言
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/web3/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-web3"></i> <br />web3
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/post/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />归档
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/about/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />关于
          </a>
        </li>
      
      <li class="menu-item menu-item-search">
        <a href="javascript:;" class="popup-trigger"> <i class="menu-item-icon fa fa-search fa-fw"></i> <br /> 搜索</a>
      </li>
    </ul>
    <div class="site-search">
      <div class="popup">
 <span class="search-icon fa fa-search"></span>
 <input type="text" id="local-search-input">
 <div id="local-search-result"></div>
 <span class="popup-btn-close">close</span>
</div>

    </div>
</nav>

 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            
<section id="posts" class="posts-expand">
  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
    <header class="post-header">
      <h1 class="post-title" itemprop="name headline">
        <a class="post-title-link" href="http://www.shutdown.cn/post/redis5.0%E6%96%B0%E7%89%B9%E6%80%A7-stream%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B/" itemprop="url">
        
        </a>
      </h1>
      <div class="post-meta">
      <span class="post-time">
<span class="post-meta-item-icon">
    <i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">时间：</span>
<time itemprop="dateCreated" datetime="2016-03-22T13:04:35+08:00" content="0001-01-01">
    0001-01-01
</time>
</span> 
      
      
       <span>
&nbsp; | &nbsp;
<span class="post-meta-item-icon">
    <i class="fa fa-eye"></i>
</span>
<span class="post-meta-item-text">阅读：</span>
<span class="leancloud-visitors-count">1706 字 ~9分钟</span>
</span>
      </div>
    </header>
    <div class="post-body" itemprop="articleBody">
    

    

<blockquote>
<p>原文地址：<a href="https://juejin.cn/post/7028439051308892167">https://juejin.cn/post/7028439051308892167</a></p>
</blockquote>

<h2 id="一-背景">一、背景</h2>

<p>最近在看<code>redis</code>这方面的知识，发现在<code>redis5</code>中产生了一种新的数据类型<code>Stream</code>，它和<code>kafka</code>的设计有些类似，可以当作一个简单的消息队列来使用。</p>

<h2 id="二-redis中stream类型的特点">二、redis中Stream类型的特点</h2>

<ol>
<li>支持持久化。streams能持久化存储数据，不同于pub/sub机制和list 消息被消费后就会被删除，streams消费过的数据会被持久化的保存在历史中。</li>
<li>支持消息的多播、分组消费。 这一点跟 pub/sub有些类似。</li>
<li>支持消息的有序性，可以通过消息的ID来控制消息的排序。</li>
<li>支持消费者组。streams 允许同一消费组内的消费者竞争消息，并提供了一系列机制允许消费者查看自己的历史消费消息。并允许监控streams的消费者组信息，消费者组内消费者信息，也可以监控streams内消息的状态。</li>
</ol>

<h2 id="三-stream的结构">三、Stream的结构</h2>

<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220616175901.png" width = 900 /> </div>

<p><strong>解释：</strong>
1. <code>消费者组</code>：Consumer Group,即使用<code>XGROUP CREATE</code>命令创建的，一个消费者组中可以存在多个消费者，这些消费者之间是竞争关系。
   1. 同一条消息，只能被这个消费者组中的某个消费者获取。
   2. 多个消费者之间是相互独立的，互不干扰。
2. <code>消费者</code>： Consumer 消费消息。
3. <code>last_delivered_id</code>： 这个id保证了在同一个消费者组中，一个消息只能被一个消费者获取。每当消费者组的某个消费者读取到了这个消息后，这个last_delivered_id的值会往后移动一位，保证消费者不会读取到重复的消息。
4. <code>pending_ids</code>：记录了消费者读取到的消息id列表，但是这些消息可能还没有处理，如果认为某个消息处理，需要调用<code>ack</code>命令。这样就确保了某个消息一定会被执行一次。
5. <code>消息内容：</code>是一个<code>键值对</code>的格式。
6. <code>Stream 中 消息的 ID：</code> 默认情况下，ID使用 <code>*</code> ，redis可以自动生成一个，格式为 <code>时间戳-序列号</code>，也可以自己指定，一般使用默认生成的即可，且后生成的id号要比之前生成的大。</p>

<h2 id="四-stream的命令">四、Stream的命令</h2>

<h3 id="1-xadd-往stream末尾添加消息">1、XADD 往Stream末尾添加消息</h3>

<h4 id="1-命令格式">1、命令格式</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">xadd key <span style="color:#ce5c00;font-weight:bold">[</span>NOMKSTREAM<span style="color:#ce5c00;font-weight:bold">]</span> <span style="color:#ce5c00;font-weight:bold">[</span>MAXLEN<span style="color:#000;font-weight:bold">|</span>MINID <span style="color:#ce5c00;font-weight:bold">[</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000;font-weight:bold">|</span>~<span style="color:#ce5c00;font-weight:bold">]</span> threshold <span style="color:#ce5c00;font-weight:bold">[</span>LIMIT count<span style="color:#ce5c00;font-weight:bold">]</span><span style="color:#ce5c00;font-weight:bold">]</span> *<span style="color:#000;font-weight:bold">|</span>ID field value <span style="color:#ce5c00;font-weight:bold">[</span>field value ...<span style="color:#ce5c00;font-weight:bold">]</span></code></pre></div>
<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220616180557.png" width = 900 />xadd命令解释 </div>

<h4 id="2-举例">2、举例</h4>

<p>xadd 命令 返回的是数据的id, <strong>xx-yy</strong> (xx指的是毫秒数,yy指的是在这个毫秒内的第几条消息)</p>

<h5 id="1-向流中增加一条数据">1、向流中增加一条数据,</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xadd stream-key * username zhangsan <span style="color:#8f5902;font-style:italic"># 向stream-key这个流中增加一个 username 是zhangsan的数据 *表示自动生成id</span>
<span style="color:#4e9a06">&#34;1635999858912-0&#34;</span> <span style="color:#8f5902;font-style:italic"># 返回的是ID</span>
127.0.0.1:6379&gt; keys *
1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;stream-key&#34;</span> <span style="color:#8f5902;font-style:italic"># 可以看到stream自动创建了</span>
127.0.0.1:6379&gt;</code></pre></div>
<h5 id="2-向流中增加数据-不自动创建流">2、向流中增加数据，不自动创建流</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xadd not-exists-stream nomkstream * username lisi <span style="color:#8f5902;font-style:italic"># 因为指定了nomkstream参数，而not-exists-stream之前不存在，所以加入失败</span>
<span style="color:#ce5c00;font-weight:bold">(</span>nil<span style="color:#ce5c00;font-weight:bold">)</span>
127.0.0.1:6379&gt; keys *
<span style="color:#ce5c00;font-weight:bold">(</span>empty array<span style="color:#ce5c00;font-weight:bold">)</span>
127.0.0.1:6379&gt;</code></pre></div>
<h5 id="3-手动指定id的值">3、手动指定ID的值</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xadd stream-key 1-1 username lisi <span style="color:#8f5902;font-style:italic"># 此处id的值是自己传递的1-1,而不是使用*自动生成</span>
<span style="color:#4e9a06">&#34;1-1&#34;</span> <span style="color:#8f5902;font-style:italic"># 返回的是id的值</span>
127.0.0.1:6379&gt;</code></pre></div>
<h5 id="4-设置一个固定大小的stream">4、设置一个固定大小的Stream</h5>

<h6 id="1-精确指定stream的大小">1、精确指定Stream的大小</h6>

<p>指定指定Stream的大小比模糊指定Stream的大小会稍微多少消耗一些性能。</p>

<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220616191300.png" width = 800 /> </div>

<h6 id="2-模糊指定stream的大小">2、模糊指定Stream的大小</h6>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xadd stream-key maxlen ~ <span style="color:#0000cf;font-weight:bold">1</span> * first first
<span style="color:#4e9a06">&#34;1636001034141-0&#34;</span>
127.0.0.1:6379&gt; xadd stream-key maxlen ~ <span style="color:#0000cf;font-weight:bold">1</span> * second second
<span style="color:#4e9a06">&#34;1636001044506-0&#34;</span>
127.0.0.1:6379&gt; xadd stream-key maxlen ~ <span style="color:#0000cf;font-weight:bold">1</span> * third third
<span style="color:#4e9a06">&#34;1636001057846-0&#34;</span>
127.0.0.1:6379&gt; xinfo stream stream-key
 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;length&#34;</span>
 2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">3</span>
 3<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;radix-tree-keys&#34;</span>
 4<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">1</span>
 5<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;radix-tree-nodes&#34;</span>
 6<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">2</span>
 7<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;last-generated-id&#34;</span>
 8<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636001057846-0&#34;</span>
 9<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;groups&#34;</span>
10<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">0</span>
11<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;first-entry&#34;</span>
12<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636001034141-0&#34;</span>
    2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;first&#34;</span>
       2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;first&#34;</span>
13<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;last-entry&#34;</span>
14<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636001057846-0&#34;</span>
    2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;third&#34;</span>
       2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;third&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<p><code>~</code> 模糊指定流的大小，可以看到指定的是1，实际上已经到了3.</p>

<h3 id="2-xrange查看stream中的消息">2、XRANGE查看Stream中的消息</h3>

<h4 id="1-命令格式-1">1、命令格式</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">xrange key start end <span style="color:#ce5c00;font-weight:bold">[</span>COUNT count<span style="color:#ce5c00;font-weight:bold">]</span></code></pre></div>
<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220616191332.png" width = 900 /> </div>

<h4 id="2-准备数据">2、准备数据</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; multi
OK
127.0.0.1:6379<span style="color:#ce5c00;font-weight:bold">(</span>TX<span style="color:#ce5c00;font-weight:bold">)</span>&gt; xadd stream-key * username zhangsan
QUEUED
127.0.0.1:6379<span style="color:#ce5c00;font-weight:bold">(</span>TX<span style="color:#ce5c00;font-weight:bold">)</span>&gt; xadd stream-key * username lisi
QUEUED
127.0.0.1:6379<span style="color:#ce5c00;font-weight:bold">(</span>TX<span style="color:#ce5c00;font-weight:bold">)</span>&gt; <span style="color:#204a87">exec</span>
1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636003481706-0&#34;</span>
2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636003481706-1&#34;</span>
127.0.0.1:6379&gt; xadd stream-key * username wangwu
<span style="color:#4e9a06">&#34;1636003499055-0&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<p>使用redis的事务操作，获取到同一毫秒产生的多条数据，时间戳一样，序列号不一样</p>

<h4 id="3-举例">3、举例</h4>

<h5 id="1-获取所有的数据-和-的使用">1、获取所有的数据(<code>-</code>和<code>+</code>的使用)</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xrange stream-key - +
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636003481706-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;zhangsan&#34;</span>
2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636003481706-1&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;lisi&#34;</span>
3<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636003499055-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;wangwu&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<p><code>-:</code> 表示最小id的值</p>

<p><code>+:</code>表示最大id的值</p>

<h5 id="2-获取指定id范围内的数据-闭区间">2、获取指定id范围内的数据，闭区间</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xrange stream-key 1636003481706-1 1636003499055-0
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636003481706-1&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;lisi&#34;</span>
2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636003499055-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;wangwu&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<h5 id="3-获取指定id范围内的数据-开区间">3、获取指定id范围内的数据，开区间</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xrange stream-key <span style="color:#ce5c00;font-weight:bold">(</span>1636003481706-0 <span style="color:#ce5c00;font-weight:bold">(</span>1636003499055-0
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636003481706-1&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;lisi&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<p><code>(:</code>表示开区间</p>

<h5 id="4-获取某个毫秒后所有的数据">4、获取某个毫秒后所有的数据</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xrange stream-key <span style="color:#0000cf;font-weight:bold">1636003481706</span> +
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636003481706-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;zhangsan&#34;</span>
2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636003481706-1&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;lisi&#34;</span>
3<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636003499055-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;wangwu&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<p>直接写<code>毫秒</code>不写后面的序列号即可。</p>

<h5 id="5-获取单条数据">5、获取单条数据</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xrange stream-key 1636003499055-0 1636003499055-0
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636003499055-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;wangwu&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<p><code>start</code>和<code>end</code>的值写的一样即可获取单挑数据。</p>

<h5 id="6-获取固定条数的数据">6、获取固定条数的数据</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xrange stream-key - + count <span style="color:#0000cf;font-weight:bold">1</span>
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636003481706-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;zhangsan&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<p>使用 <code>count</code>进行限制</p>

<h3 id="3-xrevrange反向查看stream中的消息">3、XREVRANGE反向查看Stream中的消息</h3>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">XREVRANGE key end start <span style="color:#ce5c00;font-weight:bold">[</span>COUNT count<span style="color:#ce5c00;font-weight:bold">]</span></code></pre></div>
<p>使用方式和<code>XRANGE</code>类似，略。</p>

<h3 id="4-xdel删除消息">4、XDEL删除消息</h3>

<h4 id="1-命令格式-2">1、命令格式</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">xdel key ID <span style="color:#ce5c00;font-weight:bold">[</span>ID ...<span style="color:#ce5c00;font-weight:bold">]</span></code></pre></div>
<h4 id="2-准备数据-1">2、准备数据</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xadd stream-key * username zhangsan
<span style="color:#4e9a06">&#34;1636004176924-0&#34;</span>
127.0.0.1:6379&gt; xadd stream-key * username lisi
<span style="color:#4e9a06">&#34;1636004183638-0&#34;</span>
127.0.0.1:6379&gt; xadd stream-key * username wangwu
<span style="color:#4e9a06">&#34;1636004189211-0&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<h4 id="3-举例-1">3、举例</h4>

<p><strong>需求：</strong> 往Stream中加入3条消息，然后删除第2条消息</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xdel stream-key 1636004183638-0
<span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">1</span> <span style="color:#8f5902;font-style:italic"># 返回的是删除记录的数量</span>
127.0.0.1:6379&gt; xrang stream -key - +
127.0.0.1:6379&gt; xrange stream-key - +
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636004176924-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;zhangsan&#34;</span>
2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636004189211-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;wangwu&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<p><strong>注意：</strong></p>

<p>需要注意的是，我们从Stream中删除一个消息，这个消息并不是被真正的删除了，而是被<strong>标记为删除</strong>，这个时候这个消息还是占据着内容空间的。如果所有Stream中所有的消息都被标记删除，这个时候才会回收内存空间。但是这个Stream并不会被删除。</p>

<h3 id="6-xlen查看stream中元素的长度">6、XLEN查看Stream中元素的长度</h3>

<h4 id="1-命令格式-3">1、命令格式</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">xlen key</code></pre></div>
<h4 id="2-举例-1">2、举例</h4>

<p>查看Stream中元素的长度</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xadd stream-key * username zhangsan
<span style="color:#4e9a06">&#34;1636004690578-0&#34;</span>
127.0.0.1:6379&gt; xlen stream-key
<span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">1</span>
127.0.0.1:6379&gt; xlen not-exists-stream-key
<span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">0</span>
127.0.0.1:6379&gt;</code></pre></div>
<p>注意：</p>

<p>如果<code>xlen</code>后方的<code>key</code>不存在则返回0，否则返回元素的个数。</p>

<h3 id="7-xtrim对stream中的元素进行修剪">7、XTRIM对Stream中的元素进行修剪</h3>

<h4 id="1-命令格式-4">1、命令格式</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">xtrim key MAXLEN<span style="color:#000;font-weight:bold">|</span>MINID <span style="color:#ce5c00;font-weight:bold">[</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000;font-weight:bold">|</span>~<span style="color:#ce5c00;font-weight:bold">]</span> threshold <span style="color:#ce5c00;font-weight:bold">[</span>LIMIT count<span style="color:#ce5c00;font-weight:bold">]</span></code></pre></div>
<h4 id="2-准备数据-2">2、准备数据</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt;  xadd stream-key * username zhangsan
<span style="color:#4e9a06">&#34;1636009745401-0&#34;</span>
127.0.0.1:6379&gt; multi
OK
127.0.0.1:6379<span style="color:#ce5c00;font-weight:bold">(</span>TX<span style="color:#ce5c00;font-weight:bold">)</span>&gt; xadd stream-key * username lisi
QUEUED
127.0.0.1:6379<span style="color:#ce5c00;font-weight:bold">(</span>TX<span style="color:#ce5c00;font-weight:bold">)</span>&gt; xadd stream-key * username wangwu
QUEUED
127.0.0.1:6379<span style="color:#ce5c00;font-weight:bold">(</span>TX<span style="color:#ce5c00;font-weight:bold">)</span>&gt; <span style="color:#204a87">exec</span>
1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636009763955-0&#34;</span>
2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636009763955-1&#34;</span>
127.0.0.1:6379&gt; xadd stream-key * username zhaoliu
<span style="color:#4e9a06">&#34;1636009769625-0&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<h4 id="3-举例-2">3、举例</h4>

<h5 id="1-maxlen精确限制">1、maxlen精确限制</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xtrim stream-key maxlen <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#8f5902;font-style:italic"># 保留最后的2个消息</span>
<span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">2</span>
127.0.0.1:6379&gt; xrange stream-key - + <span style="color:#8f5902;font-style:italic"># 可以看到之前加入的2个消息被删除了</span>
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636009763955-1&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;wangwu&#34;</span>
2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636009769625-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;zhaoliu&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<p>上方的意思是，保留<code>stream-key</code>这个Stream中最后的2个消息。</p>

<h5 id="2-minid模糊限制">2、minid模糊限制</h5>

<p>minid 是删除比这个id小的数据，本地测试的时候<code>没有测试出来</code>，略。</p>

<h3 id="8-xread独立消费消息">8、XREAD独立消费消息</h3>

<p><code>XREAD</code>只是读取消息，读取完之后并不会删除消息。 使用<code>XREAD</code>读取消息，是完全独立与消费者组的，多个客户端可以同时读取消息。</p>

<h4 id="1-命令格式-5">1、命令格式</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">xread <span style="color:#ce5c00;font-weight:bold">[</span>COUNT count<span style="color:#ce5c00;font-weight:bold">]</span> <span style="color:#ce5c00;font-weight:bold">[</span>BLOCK milliseconds<span style="color:#ce5c00;font-weight:bold">]</span> STREAMS key <span style="color:#ce5c00;font-weight:bold">[</span>key ...<span style="color:#ce5c00;font-weight:bold">]</span> ID <span style="color:#ce5c00;font-weight:bold">[</span>ID ...<span style="color:#ce5c00;font-weight:bold">]</span></code></pre></div>
<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220616191401.png" width = 900 /> </div>

<h4 id="2-准备数据-3">2、准备数据</h4>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xadd stream-key * username zhangsan
<span style="color:#4e9a06">&#34;1636011801365-0&#34;</span>
127.0.0.1:6379&gt; xadd stream-key * username lisi
<span style="color:#4e9a06">&#34;1636011806261-0&#34;</span>
127.0.0.1:6379&gt; xadd stream-key * username wangwu
<span style="color:#4e9a06">&#34;1636011810905-0&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<h4 id="3-举例-3">3、举例</h4>

<h5 id="1-获取用户名是wangwu的数据">1、获取用户名是wangwu的数据</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xread streams stream-key 1636011806261-0 <span style="color:#8f5902;font-style:italic"># 此处写的是lisi的id，即读取到的数据需要是 &gt; 1636011806261-0</span>
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;stream-key&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636011810905-0&#34;</span>
         2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
            2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;wangwu&#34;</span></code></pre></div>
<h5 id="2-获取2条数据">2、获取2条数据</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xread count <span style="color:#0000cf;font-weight:bold">2</span> streams stream-key 0-0
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;stream-key&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636011801365-0&#34;</span>
         2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
            2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;zhangsan&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636011806261-0&#34;</span>
         2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;username&#34;</span>
            2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;lisi&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<p><code>count</code>限制单次读取最后的消息，因为当前读取可能没有这么多。</p>

<h5 id="3-非阻塞读取stream对尾的数据">3、非阻塞读取Stream对尾的数据</h5>

<p>即读取队列尾的下一个消息，在非阻塞模式下始终是<code>nil</code></p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xread streams stream-key $
<span style="color:#ce5c00;font-weight:bold">(</span>nil<span style="color:#ce5c00;font-weight:bold">)</span></code></pre></div>
<h5 id="4-阻塞读取stream对尾的数据">4、阻塞读取Stream对尾的数据</h5>

<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220616191424.png" width = 900 /> </div>

<p><strong>注意：</strong></p>

<ol>
<li><code>$</code>表示读取队列最新进来的一个消息，不是Stream的最后一个消息。是<code>xread block</code>执行后，再次使用<code>xadd</code>添加消息后，<code>xread block</code>才会返回。</li>
<li><code>block 0</code>表示永久阻塞，当消息到来时，才接触阻塞。<code>block 1000</code>表示阻塞1000ms，如果1000ms还没有消息到来，则返回<code>nil</code></li>
<li><code>xread进行顺序消费</code> 当使用xread进行顺序消息时，需要记住返回的消息id，同时下次调用xread时，需要将上次返回的消息id传递进去。</li>
<li><code>xread</code>读取消息，完全无视消费组，此时<code>Stream</code>就可以理解为一个普通的list。</li>
</ol>

<h4 id="4-对比-pub-sub">4、对比<code>pub/sub</code></h4>

<p>XREAD允许我们从某一结点开始从streams中读取数据，它的语法为<code>XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]</code>，我们在这里主要将的是通过<code>XREAD</code>来订阅到达streams新的数据。这种操作可能跟REDIS中原有的<code>pub/sub</code>机制或者<code>阻塞队列</code>的概念有些类似，都是等待一个key然后获取到新的数据，但是跟这两种有着本质的差别：</p>

<ul>
<li>streams跟<code>pub/sub</code>和<code>阻塞队列</code>允许多个客户端一起等待数据，默认情况下，streams会把消息推送给所有等待streams数据的客户端，这个能力跟<code>pub/sub</code>有点类似，但是streams也允许把消息通过竞争机制推送给其中的一个客户端（这种模式需要用到消费者组的概念，会在后边讲到）。</li>
<li><code>pub/sub</code>的消息是fire and forget并且从不存储，你只可以订阅到在你订阅时间之后产生的消息，并且消息只会推送给客户端一次，不能查看历史记录。以及使用<code>阻塞队列</code>时，当客户端收到消息时，这个元素会从队列中弹出，换句话说，不能查看某个消费者消费消息的历史。而在streams中所有的消息会被无限期的加入到streams中（消息可以被显式的删除并且存在淘汰机制），客户端需要记住收到的最后一条消息，用于获取到节点之后的新消息。</li>
<li>Streams 消费者组提供了一种Pub/Sub或者阻塞列表都不能实现的控制级别，同一个Stream不同的群组，显式地确认已经处理的项目，检查待处理的项目的能力，申明未处理的消息，以及每个消费者拥有连贯历史可见性，单个客户端只能查看自己过去的消息历史记录。
从streams中读取数据127.0.0.1:6379&gt; XREAD COUNT 2 STREAMS mystream 0 1) 1) &ldquo;mystream&rdquo; 2) 1) 1) &ldquo;1574835253335-0&rdquo; 2) 1) &ldquo;name&rdquo; 2) &ldquo;bob&rdquo; 3) &ldquo;age&rdquo; 4) &ldquo;23&rdquo; 2) 1) &ldquo;1574925508730-0&rdquo; 2) 1) &ldquo;name&rdquo; 2) &ldquo;dwj&rdquo; 3) &ldquo;age&rdquo; 4) &ldquo;18&rdquo; 127.0.0.1:6379&gt;
同list结构一样，streams也提供了阻塞读取的命令XREAD BLOCK 0 STREAMS mystream
在上边的命令中指定了BLOCK选项，超时时间为0毫秒（意味着永不会过期）。此外，这个地方使用了特殊的id <code>$</code>，这个特殊的id代表着当前streams中最大的id，这就意味着你只会读取streams中在你监听时间以后的消息。有点类似于Unix的<code>tail -f</code>。另外XREAD可以同时监听多个流中的数据。</li>
</ul>

<h4 id="9-消费者组相关操作">9、消费者组相关操作</h4>

<p>如果我们想要的不是多个客户端处理相同的消息，而是多个客户端从streams中获取到不同的消息进行处理。也就是我们常用的生产者-消费者模型。假如想象我们具有两个生产者p1,p2，三个消费者c1,c2,c3以及7个商品。我们想按照下面的效果进行处理</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">p1 =&gt;item1 =&gt; c1
p2 =&gt;item2 =&gt; c2
p1 =&gt;item3 =&gt; c3
p2 =&gt;item4 =&gt; c1
p1 =&gt;item5 =&gt; c2
p2 =&gt;item6 =&gt; c3
p1 =&gt;item7 =&gt; c1</code></pre></div>
<p>为了解决这种场景，redis使用了一个名为消费者的概念，有点类似于kafka，但只是表现上。消费者组就像是一个伪消费者，它从流内读取数据，然后分发给组内的消费者，并记录该消费者组消费了哪些数据，处理了那些数据，并提供了一系列功能。</p>

<ol>
<li>每条消息都提供给不同的消费者，因此不可能将相同的消息传递给多个消费者。</li>
<li>消费者在消费者组中通过名称来识别，该名称是实施消费者的客户必须选择的区分大小写的字符串。这意味着即便断开连接过后，消费者组仍然保留了所有的状态，因为客户端会重新申请成为相同的消费者。 然而，这也意味着由客户端提供唯一的标识符。</li>
<li>每一个消费者组都有一个第一个ID永远不会被消费的概念，这样一来，当消费者请求新消息时，它能提供以前从未传递过的消息。</li>
<li>消费消息需要使用特定的命令进行显式确认，表示：这条消息已经被正确处理了，所以可以从消费者组中逐出。</li>
<li>消费者组跟踪所有当前所有待处理的消息，也就是，消息被传递到消费者组的一些消费者，但是还没有被确认为已处理。由于这个特性，当访问一个Stream的历史消息的时候，每个消费者将只能看到传递给它的消息。</li>
</ol>

<p>它的模型类似于如下</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#000;font-weight:bold">|</span> consumer_group_name: mygroup           <span style="color:#000;font-weight:bold">|</span>
<span style="color:#000;font-weight:bold">|</span> consumer_group_stream: somekey         <span style="color:#000;font-weight:bold">|</span>
<span style="color:#000;font-weight:bold">|</span> last_delivered_id: 1292309234234-92    <span style="color:#000;font-weight:bold">|</span>
<span style="color:#000;font-weight:bold">|</span>                                        <span style="color:#000;font-weight:bold">|</span>
<span style="color:#000;font-weight:bold">|</span> consumers:                             <span style="color:#000;font-weight:bold">|</span>
<span style="color:#000;font-weight:bold">|</span>    <span style="color:#4e9a06">&#34;consumer-1&#34;</span> with pending messages  <span style="color:#000;font-weight:bold">|</span>
<span style="color:#000;font-weight:bold">|</span>       1292309234234-4                  <span style="color:#000;font-weight:bold">|</span>
<span style="color:#000;font-weight:bold">|</span>       1292309234232-8                  <span style="color:#000;font-weight:bold">|</span>
<span style="color:#000;font-weight:bold">|</span>    <span style="color:#4e9a06">&#34;consumer-42&#34;</span> with pending messages <span style="color:#000;font-weight:bold">|</span>
<span style="color:#000;font-weight:bold">|</span>       ... <span style="color:#ce5c00;font-weight:bold">(</span>and so forth<span style="color:#ce5c00;font-weight:bold">)</span>               <span style="color:#000;font-weight:bold">|</span></code></pre></div>
<p>从上边的模型中我们可以看出消费者组记录处理的最后一条消息，将消息分发给不同的消费者，每个消费者只能看到自己的消息。如果把消费者组看做streams的辅助数据结构，我们可以看出一个streams可以拥有多个消费者组，一个消费者组内可以拥有多个消费者。实际上，一个streams允许客户端使用XREAD读取的同时另一个客户端通过消费者群组读取数据。</p>

<h5 id="1-消费者组命令">1、消费者组命令</h5>

<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220616191444.png" width = 900 /> </div>

<h5 id="2-准备数据-4">2、准备数据</h5>

<p>1、创建Stream的名称是 stream-key</p>

<p>2、创建2个消息，aa和bb</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xadd stream-key * aa aa
<span style="color:#4e9a06">&#34;1636362619125-0&#34;</span>
127.0.0.1:6379&gt; xadd stream-key * bb bb
<span style="color:#4e9a06">&#34;1636362623191-0&#34;</span></code></pre></div>
<h5 id="3-创建消费者组">3、创建消费者组</h5>

<h6 id="1-创建一个从头开始消费的消费者组">1、创建一个从头开始消费的消费者组</h6>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-fallback" data-lang="fallback">xgroup create stream-key(Stream 名) g1(消费者组名) 0-0(表示从头开始消费)</code></pre></div>
<h6 id="2-创建一个从stream最新的一个消息消费的消费者组">2、创建一个从Stream最新的一个消息消费的消费者组</h6>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-fallback" data-lang="fallback">xgroup create stream-key g2 $</code></pre></div>
<p><code>$</code>表示从最后一个元素消费，不包括Stream中的最后一个元素，即消费最新的消息。</p>

<h6 id="4-创建一个从某个消息之后消费的消费者组">4、创建一个从某个消息之后消费的消费者组</h6>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-fallback" data-lang="fallback">xgroup create stream-key g3 1636362619125-0  #1636362619125-0 这个是上方aa消息的id的值</code></pre></div>
<p><code>1636362619125-0</code>某个消息的具体的ID,这个<code>g3</code>消费者组中的消息都是<code>大于&gt;</code>这个id的消息。</p>

<h5 id="3-从消费者中读取消息">3、从消费者中读取消息</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xreadgroup group g1<span style="color:#ce5c00;font-weight:bold">(</span>消费组名<span style="color:#ce5c00;font-weight:bold">)</span> c1<span style="color:#ce5c00;font-weight:bold">(</span>消费者名，自动创建<span style="color:#ce5c00;font-weight:bold">)</span> count 3<span style="color:#ce5c00;font-weight:bold">(</span>读取3条<span style="color:#ce5c00;font-weight:bold">)</span> streams stream-key<span style="color:#ce5c00;font-weight:bold">(</span>Stream 名<span style="color:#ce5c00;font-weight:bold">)</span> &gt;<span style="color:#ce5c00;font-weight:bold">(</span>从该消费者组中还未分配给另外的消费者的消息开始读取<span style="color:#ce5c00;font-weight:bold">)</span>
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;stream-key&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636362619125-0&#34;</span>
         2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;aa&#34;</span>
            2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;aa&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636362623191-0&#34;</span>
         2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;bb&#34;</span>
            2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;bb&#34;</span>
127.0.0.1:6379&gt; xreadgroup group g2 c1 count <span style="color:#0000cf;font-weight:bold">3</span> streams stream-key &gt;
<span style="color:#ce5c00;font-weight:bold">(</span>nil<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#8f5902;font-style:italic"># 返回 nil 是因为 g2消费组是从最新的一条信息开始读取(创建消费者组时使用了$)，需要在另外的窗口执行`xadd`命令，才可以再次读取到消息</span>
127.0.0.1:6379&gt; xreadgroup group g3 c1 count <span style="color:#0000cf;font-weight:bold">3</span> streams stream-key &gt;  <span style="color:#8f5902;font-style:italic">#只读取到一条消息是因为，在创建消费者组时，指定了aa消息的id，bb消息的id大于aa,所以读取出来了。</span>
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;stream-key&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636362623191-0&#34;</span>
         2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;bb&#34;</span>
            2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;bb&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<h5 id="4-读取消费者的pending消息">4、读取消费者的pending消息</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xgroup create stream-key g4 0-0
OK
127.0.0.1:6379&gt; xinfo consumers stream-key g1
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;name&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;c1&#34;</span>
   3<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;pending&#34;</span>
   4<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">2</span>
   5<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;idle&#34;</span>
   6<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">88792</span>
127.0.0.1:6379&gt; xinfo consumers stream-key g4
<span style="color:#ce5c00;font-weight:bold">(</span>empty array<span style="color:#ce5c00;font-weight:bold">)</span>
127.0.0.1:6379&gt; xreadgroup group g1 c1 count <span style="color:#0000cf;font-weight:bold">1</span> streams stream-key 1636362619125-0
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;stream-key&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636362623191-0&#34;</span>
         2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;bb&#34;</span>
            2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;bb&#34;</span>
127.0.0.1:6379&gt; xreadgroup group g4 c1 count <span style="color:#0000cf;font-weight:bold">1</span> block <span style="color:#0000cf;font-weight:bold">0</span> streams stream-key 1636362619125-0
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;stream-key&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>empty array<span style="color:#ce5c00;font-weight:bold">)</span>
127.0.0.1:6379&gt;</code></pre></div>
<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220616191505.png" width = 900 /> </div>

<h5 id="5-转移消费者的消息">5、转移消费者的消息</h5>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xpending stream-key g1 - + <span style="color:#0000cf;font-weight:bold">10</span> c1
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636362619125-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;c1&#34;</span>
   3<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">2686183</span>
   4<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">1</span>
2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636362623191-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;c1&#34;</span>
   3<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">102274</span>
   4<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">7</span>
127.0.0.1:6379&gt; xpending stream-key g1 - + <span style="color:#0000cf;font-weight:bold">10</span> c2
<span style="color:#ce5c00;font-weight:bold">(</span>empty array<span style="color:#ce5c00;font-weight:bold">)</span>
127.0.0.1:6379&gt; xclaim stream-key g1 c2 <span style="color:#0000cf;font-weight:bold">102274</span> 1636362623191-0
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636362623191-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;bb&#34;</span>
      2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;bb&#34;</span>
127.0.0.1:6379&gt; xpending stream-key g1 - + <span style="color:#0000cf;font-weight:bold">10</span> c2
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636362623191-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;c2&#34;</span>
   3<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">17616</span>
   4<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">8</span>
127.0.0.1:6379&gt;</code></pre></div>
<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220616191520.png" width = 900 /> </div>

<p>也可以通过<code>xautoclaim</code>来实现。</p>

<h5 id="6-一些监控命令">6、一些监控命令</h5>

<h6 id="1-查看消费组中消费者的pending消息">1、查看消费组中消费者的pending消息</h6>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xpending stream-key g1 - + <span style="color:#0000cf;font-weight:bold">10</span> c2
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636362623191-0&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;c2&#34;</span>
   3<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">1247680</span>
   4<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">8</span>
127.0.0.1:6379&gt;</code></pre></div>
<h6 id="2-查看消费组中的消费者信息">2、查看消费组中的消费者信息</h6>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xinfo consumers stream-key g1
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;name&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;c1&#34;</span>
   3<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;pending&#34;</span>
   4<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">1</span>
   5<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;idle&#34;</span>
   6<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">1474864</span>
2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;name&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;c2&#34;</span>
   3<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;pending&#34;</span>
   4<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">1</span>
   5<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;idle&#34;</span>
   6<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">1290069</span>
127.0.0.1:6379&gt;</code></pre></div>
<h6 id="3-查看消费组信息">3、查看消费组信息</h6>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xinfo groups stream-key
1<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;name&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;g1&#34;</span>
   3<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;consumers&#34;</span>
   4<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">2</span>
   5<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;pending&#34;</span>
   6<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">2</span>
   7<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;last-delivered-id&#34;</span>
   8<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636362623191-0&#34;</span>
2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;name&#34;</span>
   2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;g2&#34;</span>
   3<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;consumers&#34;</span>
......</code></pre></div>
<h6 id="4-查看stream信息">4、查看Stream信息</h6>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">127.0.0.1:6379&gt; xinfo stream stream-key
 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;length&#34;</span>
 2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">2</span>
 3<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;radix-tree-keys&#34;</span>
 4<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">1</span>
 5<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;radix-tree-nodes&#34;</span>
 6<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">2</span>
 7<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;last-generated-id&#34;</span>
 8<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636362623191-0&#34;</span>
 9<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;groups&#34;</span>
10<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">(</span>integer<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#0000cf;font-weight:bold">4</span>
11<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;first-entry&#34;</span>
12<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636362619125-0&#34;</span>
    2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;aa&#34;</span>
       2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;aa&#34;</span>
13<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;last-entry&#34;</span>
14<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;1636362623191-0&#34;</span>
    2<span style="color:#ce5c00;font-weight:bold">)</span> 1<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;bb&#34;</span>
       2<span style="color:#ce5c00;font-weight:bold">)</span> <span style="color:#4e9a06">&#34;bb&#34;</span>
127.0.0.1:6379&gt;</code></pre></div>
<h5 id="7-有几件事需要记住">7、有几件事需要记住：</h5>

<p>消费者是在他们第一次被提及的时候自动创建的，不需要显式创建。
即使使用XREADGROUP，你也可以同时从多个key中读取，但是要让其工作，你需要给每一个Stream创建一个名称相同的消费者组。这并不是一个常见的需求，但是需要说明的是，这个功能在技术上是可以实现的。
XREADGROUP命令是一个写命令，因为当它从Stream中读取消息时，消费者组被修改了，所以这个命令只能在master节点调用。</p>

<h2 id="五-与kafka-partitions的区别">五、与kafka partitions的区别</h2>

<p>Consumer groups in Redis streams may resemble in some way Kafka &trade; partitioning-based consumer groups, however note that Redis streams are, in practical terms, very different. The partitions are only <em>logical</em> and the messages are just put into a single Redis key, so the way the different clients are served is based on who is ready to process new messages, and not from which partition clients are reading. For instance, if the consumer C3 at some point fails permanently, Redis will continue to serve C1 and C2 all the new messages arriving, as if now there are only two <em>logical</em> partitions.</p>

<blockquote>
<p>Redis streams 的消费组和Kafka的分区消费组有点像，然而Redis的streams是一个消息队列，它的消息是按照时间顺序排列的，而Kafka是一个分区的消息队列，它的消息是按照分区的顺序排列的，实际上区别很大。分区只是一个概念，消息是放在一个单一的Redis key中，不同的客户端是根据谁有新的消息来处理的，而不是根据客户端的分区来处理的。比如C3某个时候失败了，Redis会继续服务C1和C2，因为它们都有新的消息来了，而不是根据它们的分区来处理的。</p>
</blockquote>

<p>Similarly, if a given consumer is much faster at processing messages than the other consumers, this consumer will receive proportionally more messages in the same unit of time. This is possible since Redis tracks all the unacknowledged messages explicitly, and remembers who received which message and the ID of the first message never delivered to any consumer.</p>

<blockquote>
<p>如果某个消费者比其他消费者更快地处理消息，那么这个消费者将比其他消费者接收到的消息比例更高。这是可能的，因为Redis记录了所有未确认的消息，并记住了每个消费者接收到的消息和从没有消费者接收到过第一个消息的ID，。</p>
</blockquote>

<p>However, this also means that in Redis if you really want to partition messages in the same stream into multiple Redis instances, you have to use multiple keys and some sharding system such as Redis Cluster or some other application-specific sharding system. A single Redis stream is not automatically partitioned to multiple instances.</p>

<blockquote>
<p>然而，这也意味这如果你真的想要将一个消息队列分区到多个Redis实例，你需要使用多个key，并且使用一个分区算法，类似Redis集群或者一个其他的应用程序特定的分区算法。一个Redis stream不会自动分区到多个实例。</p>
</blockquote>

<p>We could say that schematically the following is true:</p>

<ul>
<li>If you use 1 stream -&gt; 1 consumer, you are processing messages in order.</li>
<li>If you use N streams with N consumers, so that only a given consumer hits a subset of the N streams, you can scale the above model of 1 stream -&gt; 1 consumer.</li>
<li>If you use 1 stream -&gt; N consumers, you are load balancing to N consumers, however in that case, messages about the same logical item may be consumed out of order, because a given consumer may process message 3 faster than another consumer is processing message 4.</li>
</ul>

<blockquote>
<p>大致效果如下:
- 如果1个stream -&gt; 1个消费者，消费消息顺序处理。
- 如果用N个streams和N个消费者，那么只有一个消费者会访问N个streams，你可以把上面的模型缩小为1个stream -&gt; 1个消费者。
- 如果1个stream -&gt; N个消费者，那么你可以负载均衡到N个消费者，但是在这种情况下，消息可能不会按顺序执行，因为一个消费者可能处理消息3比另一个消费者处理消息4慢。</p>
</blockquote>

<p>So basically Kafka partitions are more similar to using N different Redis keys, while Redis consumer groups are a server-side load balancing system of messages from a given stream to N different consumers.</p>

<blockquote>
<p>所以Kafka分区和使用N个不同的Redis keys更加相似，而Redis消费组是一个服务器端的消息负载均衡系统，从一个消息队列分区到N个不同的消费者。</p>
</blockquote>

<h2 id="六-设置streams上限">六、设置streams上限</h2>

<p>如果从streams可以查看到历史记录，我们可能会有疑惑，如果streams无限期的加入内存会不会够用，一旦消息数量达到上限，将消息永久删除或者持久化到数据库都是有必要的，redis也提供了诸如此类场景的支持。
一种方法是我们使用<code>XADD</code>的时候指定streams的最大长度，<code>XADD mystream MAXLEN ~ 1000</code>其中的数值前可以加上<code>~</code>标识不需要精确的将长度保持在1000，比1000多一些也可以接受。如果不使用该标识，性能会差一些。另一种方法是使用<code>XTRIM</code>，该命令也是使用<code>MAXLEN</code>选项，<code>&gt; XTRIM mystream MAXLEN ~ 10</code></p>

<h2 id="七-一些特殊的id">七、一些特殊的id</h2>

<p>前面提到了在streams API里边存在一些特殊的id。
首先是<code>-</code>和<code>+</code>，这两个ID在<code>XRANGE</code>命令中使用，分别代表最小的id和最大的id。<code>-</code>代表<code>0-1</code>，<code>+</code>代表<code>18446744073709551615-18446744073709551615</code>，从使用上方便了很多。在<code>XPENDING</code>等范围查询中都可以使用。
<code>$</code>代表streams中当前存在的最大的id，在<code>XREAD</code>和<code>XGROUP</code>中代表只获取新到的消息。需要注意的是<code>$</code>跟<code>+</code>的含义并不一致。
还有一个特殊的id是<code>&gt;</code>，这个id只能够在<code>XREADGROUP</code>命令中使用，意味着在这个消费者群组中，从来没有分配给其他的消费者，所以总是使用<code>&gt;</code>作为群组中的<code>last delivered ID</code>。</p>

<h2 id="八-持久化-复制和消息安全性">八、持久化，复制和消息安全性</h2>

<p>与redis的其它数据结构一样，streams会异步复制到从节点，并持久化到AOF和RDB文件中，并且消费者群组的状态也会按照此机制进行持久化。
需要注意的几点是：</p>

<ul>
<li>如果消息的持久化以及状态很重要，则AOF必须使用强fsync配合（AOF记录每一条更改redis数据的命令，有很多种持久化机制，在这个地方要用到的是<code>appendfsync always</code> 这样会严重降低Redis的速度）</li>
<li>默认情况下，异步复制不能保证从节点的数据与主节点保持一致，在故障转移以后可能会丢失一些内容，这跟从节点从主节点接受数据的能力有关。</li>
<li><code>WAIT</code>命令可以用于强制将更改传输到一组从节点上。虽然这使得数据不太可能会丢失，但是redis的Sentinel和cluster在进行故障转移的时候不一定会使用具有最新数据的从节点，在一些特殊故障下，反而会使用缺少一些数据的从节点。
因此在使用redis streams和消费者群组在设计程序的时候，确保了解你的应用程序在故障期间的应对策略，并进行相应地配置，评估它对你的程序是否足够安全。</li>
</ul>

<h2 id="九-从streams中删除数据">九、从streams中删除数据</h2>

<p>删除streams中的数据使用<code>XDEL</code>命令，其语法为<code>XDEL key ID [ID ...]</code>，需要注意的是在当前的实现中，在宏节点完全为空之前，内存并没有真正回收，所以你不应该滥用这个特性。</p>

<h2 id="十-streams的性能">十、streams的性能</h2>

<p>streams的不阻塞命令，比如<code>XRANGE</code>或者不使用BLOCK选项的<code>XREAD</code>和<code>XREADGROUP</code>跟redis普通命令一致，所以没有必要讨论。如果有兴趣的话可以在redis的文档中查看到对应命令的时间复杂度。streams命令的速度在一定范围内跟<code>set</code>是一致的，<code>XADD</code>命令的速度非常快，在一个普通的机器上，一秒钟可以插入50w~100w条数据。
我们感兴趣的是在消费者群组的阻塞场景下，从通过<code>XADD</code>命令向streams中插入一条数据，到消费者通过群组读取到这条消息的性能。
为了测试消息从产生到消费间的延迟，我们使用ruby程序进行测试，将消息的产生时间作为消息的一个字段，然后把消息推送到streams中，客户端收到消息后使用当前时间跟生产时间进行对比，从而计算出消息的延迟时间。这个程序未进行性能优化，运行在一个双核的机器上，同时redis也运行在这台机器上，以此来模拟不是理想条件下的场景。消息每秒钟产生1w条，群组内有10个消费者消费数据。测试结果如下：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">Processed between 0 and 1 ms -&gt; 74.11%
Processed between 1 and 2 ms -&gt; 25.80%
Processed between 2 and 3 ms -&gt; 0.06%
Processed between 3 and 4 ms -&gt; 0.01%
Processed between 4 and 5 ms -&gt; 0.02%</code></pre></div>
<p>99.9％的请求的延迟小于等于2毫秒，而且异常值非常接近平均值。另外需要注意的两点：</p>

<ol>
<li>消费者每次处理1w条消息，这样增加了一些延迟，这样做是为了消费速度较慢的消费者能够保持保持消息流。</li>
<li>用来做测试的系统相比于现在的系统非常慢。</li>
</ol>

<h2 id="五-参考文档">五、参考文档</h2>

<p>1、<a href="https://redis.io/topics/streams-intro">https://redis.io/topics/streams-intro</a></p>

<p>2、<a href="https://www.runoob.com/redis/redis-stream.html">https://www.runoob.com/redis/redis-stream.html</a></p>

<p>3、<a href="https://zhuanlan.zhihu.com/p/95483316">https://zhuanlan.zhihu.com/p/95483316</a></p>

<p>4、<a href="https://redis.io/docs/manual/data-types/streams/">https://redis.io/docs/manual/data-types/streams/</a></p>

<p>5、<a href="https://juejin.cn/post/7028439051308892167">https://juejin.cn/post/7028439051308892167</a></p>

    </div>
    <footer class="post-footer">
     

     <div class="post-nav">
    <div class="post-nav-next post-nav-item">
    
        <a href="http://www.shutdown.cn/post/redis5.0%E4%BC%98%E5%8C%96-%E5%86%85%E5%AD%98%E4%BD%BF%E7%94%A8%E7%BB%9F%E8%AE%A1%E6%8A%A5%E5%91%8A%E4%BC%98%E5%8C%96/" rel="next" title="">
        <i class="fa fa-chevron-left"></i> 
        </a>
    
    </div>

    <div class="post-nav-prev post-nav-item">
    
        <a href="http://www.shutdown.cn/post/redis5.0%E6%96%B0%E7%89%B9%E6%80%A7-%E9%9B%86%E7%BE%A4%E7%AE%A1%E7%90%86%E5%99%A8%E5%88%B0redis-cli/" rel="prev" title="">
         <i class="fa fa-chevron-right"></i>
        </a>
    
    </div>
</div>
      
     
     
     






    </footer>
  </article>
</section>

          </div>
        </div>
        <div class="sidebar-toggle">
  <div class="sidebar-toggle-line-wrap">
    <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
    <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
    <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
  </div>
</div>
<aside id="sidebar" class="sidebar">
  <div class="sidebar-inner">

    <section class="site-overview sidebar-panel  sidebar-panel-active ">
      <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image"
        src="http://www.shutdown.cn/img/author.jpg"
        alt="不与天斗Domino" />
    <p class="site-author-name" itemprop="name">不与天斗Domino</p>
    <p class="site-description motion-element" itemprop="description"> 
        Programmer &amp; Architect</p>
</div>
      <nav class="site-state motion-element">
    <div class="site-state-item site-state-posts">
      <a href="http://www.shutdown.cn/post/">
        <span class="site-state-item-count">183</span>
        <span class="site-state-item-name">日志</span>
      </a>
    </div>
    <div class="site-state-item site-state-categories">    
        <a href="http://www.shutdown.cn/categories/">      
         
        <span class="site-state-item-count">15</span>
        
        <span class="site-state-item-name">分类</span>
        
        </a>
    </div>

    <div class="site-state-item site-state-tags">
        <a href="http://www.shutdown.cn/tags/">
         
        <span class="site-state-item-count">224</span>
        
        <span class="site-state-item-name">标签</span>
        </a>
    </div>
</nav>
      
      

      

      <div class="links-of-blogroll motion-element inline">
<script type="text/javascript" src="//rf.revolvermaps.com/0/0/8.js?i=&amp;m=0&amp;s=220&amp;c=ff0000&amp;cr1=ffffff&amp;f=arial&amp;l=33&amp;bv=35" async="async"></script>
</div>

    </section>
    
  </div>
</aside>

      </div>
    </main>
   
    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  <span itemprop="copyrightYear">  &copy; 
  2013 - 2023</span>
  <span class="with-love"><i class="fa fa-heart"></i></span>
  <span class="author" itemprop="copyrightHolder">天地维杰网</span>
  <span class="icp" itemprop="copyrightHolder"><a href="https://beian.miit.gov.cn/" target="_blank">京ICP备13019191号-1</a></span>
</div>
<div class="powered-by">
  Powered by - <a class="theme-link" href="http://gohugo.io" target="_blank" title="hugo" >Hugo v0.63.2</a>
</div>
<div class="theme-info">
  Theme by - <a class="theme-link" href="https://github.com/xtfly/hugo-theme-next" target="_blank"> NexT
  </a>
</div>


      </div>
    </footer>

    <div class="back-to-top">
      <i class="fa fa-arrow-up"></i>
      <span id="scrollpercent"><span>0</span>%</span>
    </div>
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/jquery/index.js?v=2.1.3"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/fastclick/lib/fastclick.min.js?v=1.0.6"></script> 
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/velocity/velocity.min.js?v=1.2.1"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/velocity/velocity.ui.min.js?v=1.2.1"></script>
<script src="http://www.shutdown.cn/js/vendor/ua-parser-js/dist/ua-parser.min.js?v=0.7.9"></script>

<script src="http://www.shutdown.cn/js/vendor/fancybox/jquery.fancybox.pack.js?v=2.1.5"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/utils.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/motion.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/affix.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/schemes/pisces.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/scrollspy.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/post-details.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/toc.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/bootstrap.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/search.js"></script>
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
    extensions: ["tex2jax.js"],
    jax: ["input/TeX", "output/HTML-CSS"],
    tex2jax: {
      inlineMath: [ ['$','$'] ],
      displayMath: [ ['$$','$$'] ],
      processEscapes: true
    },
    "HTML-CSS": { fonts: ["TeX"] }
  });
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML' async></script>
</body>
</html>